/*
 * Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
 *
 * (c) Copyright 1996 - 2001 Gary Henderson (gary.henderson@ntlworld.com) and
 *                           Jerremy Koot (jkoot@snes9x.com)
 *
 * Super FX C emulator code
 * (c) Copyright 1997 - 1999 Ivar (ivar@snes9x.com) and
 *                           Gary Henderson.
 * Super FX assembler emulator code (c) Copyright 1998 zsKnight and _Demo_.
 *
 * DSP1 emulator code (c) Copyright 1998 Ivar, _Demo_ and Gary Henderson.
 * C4 asm and some C emulation code (c) Copyright 2000 zsKnight and _Demo_.
 * C4 C code (c) Copyright 2001 Gary Henderson (gary.henderson@ntlworld.com).
 *
 * DOS port code contains the works of other authors. See headers in
 * individual files.
 *
 * Snes9x homepage: http://www.snes9x.com
 *
 * Permission to use, copy, modify and distribute Snes9x in both binary and
 * source form, for non-commercial purposes, is hereby granted without fee,
 * providing that this license information and copyright notice appear with
 * all copies and any derived work.
 *
 * This software is provided 'as-is', without any express or implied
 * warranty. In no event shall the authors be held liable for any damages
 * arising from the use of this software.
 *
 * Snes9x is freeware for PERSONAL USE only. Commercial users should
 * seek permission of the copyright holders first. Commercial use includes
 * charging money for Snes9x or software derived from Snes9x.
 *
 * The copyright holders request that bug fixes and improvements to the code
 * should be forwarded to them so everyone can benefit from the modifications
 * in future versions.
 *
 * Super NES and Super Nintendo Entertainment System are trademarks of
 * Nintendo Co., Limited and its subsidiary companies.
 */

#ifdef DREAMCAST
#include <kos.h>
extern uint8 romdisk[];
KOS_INIT_FLAGS(INIT_DEFAULT);
KOS_INIT_ROMDISK(romdisk);
#endif

#include "mainwindow.h"
#include "QByteArray"
extern MainWindow * emuWindow;
#include <QMessagebox>
#include <QString>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <signal.h>
#include <errno.h>

#include <stdlib.h>
#include <string.h>
#ifndef WIN32
#include <unistd.h>
#else
#include <direct.h>
#include <string.h>
#include <io.h>
#endif
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
#include <ctype.h>
#include <dirent.h>
#include "keydef.h"

#include "snes9x.h"
#include "memmap.h"
#include "debug.h"
#include "cpuexec.h"
#include "ppu.h"
#include "snapshot.h"
#include "apu.h"
#include "display.h"
#include "gfx.h"
#include "soundux.h"
#include "spc700.h"
#include "sa1.h"



#include "interface.h"
#include "machine.h"

#define AUTO_FRAMESKIP_DELAY 2
#define TH_VCOUNTER_SPLIT 11
#define SNES_MAX_MID_VCOUNTER (SNES_MAX_NTSC_VCOUNTER+((SNES_MAX_PAL_VCOUNTER-SNES_MAX_NTSC_VCOUNTER)/2))
#define TH_VCOUNTER0 ((TH_VCOUNTER_SPLIT*SNES_MAX_PAL_VCOUNTER)/10)
#define TH_VCOUNTER1 TH_VCOUNTER0
#define TH_VCOUNTER2 ((TH_VCOUNTER_SPLIT*SNES_MAX_MID_VCOUNTER)/10)
#define TH_VCOUNTER3 ((TH_VCOUNTER_SPLIT*SNES_MAX_NTSC_VCOUNTER)/10)
#define TH_VCOUNTER4 ((TH_VCOUNTER_SPLIT*240)/10)


unsigned emu_max_vcounter=TH_VCOUNTER0;
unsigned emu_apu_hmax=1;
long emu_vcounter[512];

#if defined(DEBUG_TILECACHE) || defined(PROFILER_SNES4ALL)
int emu_frameskip=0;
int emu_sound_enable=0;
int emu_transparency=0;
int emu_throttle=0;
int emu_sound_throttle=0;
int emu_sound_rate=0;
unsigned emu_timeslice=2;

#ifdef DEBUG_TILECACHE
unsigned emu_debug_tilecache_tiles=0;
unsigned emu_debug_tilecache_fails=0;

static void print_tileche_eficience(void)
{
    double d=emu_debug_tilecache_fails;
    d*=100.0;
    d/=(double)emu_debug_tilecache_tiles;
    d=100.0-d;
    printf("%i total, %i fallados, EFICIENCIA %.2f%%\n",emu_debug_tilecache_tiles,emu_debug_tilecache_fails,d);
}
#endif


#else
int emu_frameskip=0;
bool emu_sound_enable=false;
int emu_transparency=0;
int emu_throttle=0;
int emu_state=0;
int emu_sound_rate=2;
int emu_sound_volume=9;
int emu_hires = 1;
float emu_volume = 9.0;
int emu_syncsound = 0;
int emu_intsound = 0;
int emu_framerate= 0;
int emu_stereo= 0;
int emu_sound_throttle=((1>>3)-1);
unsigned emu_timeslice=2+(3<<1);
#endif

int OldSkipFrame;

long reportf=0;

int MouseXpos=0;
int MouseYpos=0;
int MouseLbutton=0;
int MouseRbutton=0;
int SuperscopeTurbo=0;
int SuperscopeCursor=0;
quint32 MouseButton=0;

#define MUSIC_VOLUME 64

#define MAX_FRAMESKIP 6

char emu_image_file[1024];
int emu_emulating=0;
char *rom_filename = (char *)&emu_image_file[0];
char *snapshot_filename = NULL;
extern QByteArray *snesAudioBuffer;

int snesKeys[12] = { Qt::Key_Up,
                     Qt::Key_Down,
                     Qt::Key_Left,
                     Qt::Key_Right,
                     Qt::Key_A,
                     Qt::Key_B,
                     Qt::Key_X,
                     Qt::Key_Y,
                     Qt::Key_L,
                     Qt::Key_R,
                     Qt::Key_Shift,
                     Qt::Key_Return
                     };

static quint32 joypad_val[8]={0x80000000,0x80000000,0x80000000,0x80000000,0x80000000,0x80000000,0x80000000,0x80000000};

void OutOfMemory ()
{
    QMessageBox msgBox;
    msgBox.setText("out of ram.");
    msgBox.exec();
    fprintf (stderr, "\
Snes9X: Memory allocation failure - not enough RAM/virtual memory available.\n\
        S9xExiting...\n");
    Memory.Deinit ();
    S9xDeinitAPU ();
    exit (1);
}

void getChanges()
{
    if (emu_transparency){
		Settings.Transparency=TRUE;
    }else{
		Settings.Transparency=FALSE;
    }
    if (emu_intsound)
		Settings.InterpolatedSound=TRUE;
	else
		Settings.InterpolatedSound=FALSE;
    if (emu_syncsound)
		Settings.SoundSync=TRUE;
	else
		Settings.SoundSync=FALSE;
    if (emu_framerate)
		Settings.DisplayFrameRate=TRUE;
	else
		Settings.DisplayFrameRate=FALSE;

    static int chstereo = 0;

    if (emu_stereo){
        if(!Settings.Stereo)  chstereo = 1;
        Settings.Stereo=TRUE;
	}else{
        if(Settings.Stereo)  chstereo = 1;
		Settings.Stereo=FALSE;
    }

    if (!emu_frameskip)
		Settings.SkipFrames=AUTO_FRAMERATE;
	else
        Settings.SkipFrames=emu_frameskip;
#ifdef NOSOUND
    S9xSetSoundMute(!emu_sound_enable);
#endif

    if( emu_sound_enable){
        if( Settings.SoundPlaybackRate != emu_sound_rate + 1 || chstereo){
            Settings.APUEnabled=TRUE;
            Settings.SoundPlaybackRate = emu_sound_rate + 1;
            (void) S9xInitSound (Settings.SoundPlaybackRate, Settings.Stereo, Settings.SoundBufferSize);
            chstereo = 0;
        }
    }else{
        Settings.APUEnabled=FALSE;
        Settings.SoundPlaybackRate = 0;
    }
    S9xSetMasterVolume( (128 / 9)*emu_volume, (128 / 9)*emu_volume);
}

#define dCPU(A) ((unsigned)(A))-((unsigned)&CPU)
#define dSA1(A) ((unsigned)(A))-((unsigned)&SA1)
#define dIAPU(A) ((unsigned)(A))-((unsigned)&IAPU)

/*#include "cheats.h"*/
int snesInit ()
{

    ZeroMemory (&Settings, sizeof (Settings));

    Settings.C4=FALSE;
    Settings.ForceC4=FALSE;
    Settings.SoundPlaybackRate = 2;
#ifdef emu_STEREO
    Settings.Stereo = TRUE;
#else
    Settings.Stereo = FALSE;
#endif
    Settings.SoundBufferSize = 2048;
    Settings.JoystickEnabled = TRUE;
    Settings.APUEnabled = Settings.NextAPUEnabled = TRUE;
    Settings.CyclesPercentage = 100;
    Settings.H_Max = SNES_CYCLES_PER_SCANLINE;
    emu_apu_hmax=Settings.H_Max*10000L;
    Settings.SkipFrames = AUTO_FRAMERATE;
    Settings.FrameTimePAL = 20;
    Settings.FrameTimeNTSC = 17;
    Settings.FrameTime = Settings.FrameTimePAL;
    //Settings.ForcePAL = TRUE;
    Settings.Mouse = TRUE;
    Settings.SuperScope = TRUE;
    Settings.MouseMaster = TRUE;
    Settings.MultiPlayer5 = FALSE;
    Settings.ControllerOption = SNES_JOYPAD;
    Settings.Transparency = FALSE;
#ifdef NETPLAY_SUPPORT
    Settings.NetPlay = FALSE;
    Settings.ServerName [0] = 0;
#endif
    Settings.AutoSaveDelay = 1;
    Settings.DisableMasterVolume = FALSE;
    Settings.ApplyCheats = FALSE;
    Settings.TurboMode = FALSE;
    Settings.TurboSkipFrames = 15;
    Settings.NoPatch = TRUE;
    Settings.DisplayFrameRate = FALSE;
    Settings.SixteenBit = TRUE;
    Settings.SupportHiRes = TRUE;

    Settings.Shutdown = TRUE;
    Settings.ShutdownMaster = TRUE;
    Settings.SoundSync = TRUE;
    Settings.InterpolatedSound = TRUE;
    Settings.SPC7110 = TRUE;
    Settings.SPC7110RTC = TRUE;

    IAPU.APUExecuting = TRUE;
    Settings.SixteenBitSound = TRUE;
    Settings.HBlankStart = (256 * Settings.H_Max) / SNES_HCOUNTER_MAX;

    if (!Memory.Init () || !S9xInitAPU())
	OutOfMemory ();

    Settings.CyclesPercentage = (100 * SNES_MAX_PAL_VCOUNTER) / TH_VCOUNTER0;

	Settings.H_Max = (SNES_CYCLES_PER_SCANLINE * Settings.CyclesPercentage)/100;


#ifdef GFX_MULTI_FORMAT
    S9xSetRenderPixelFormat (RGB565);
#endif

    S9xInitDisplay (NULL, NULL);
    if (!S9xGraphicsInit ())
        OutOfMemory ();
#ifdef NOSOUND
    Settings.APUEnabled=FALSE;
#endif

    S9xInitInputDevices ();

    emu_image_file[0]=0;

    emu_emulating=1;

    sprintf (String, "%s: %s",TITLE, VERSION);

    S9xSetTitle (String);

    if (!Settings.APUEnabled)
        S9xSetSoundMute (FALSE);

    return 0;
}

int snesLoadRom( char* romFile){
    quint32 saved_flags = CPU.Flags;
    rom_filename = romFile;
    if (rom_filename)
    {
    if (!Memory.LoadROM (rom_filename))
	{
	    char dir [_MAX_DIR + 1];
	    char drive [_MAX_DRIVE + 1];
	    char name [_MAX_FNAME + 1];
	    char ext [_MAX_EXT + 1];
	    char fname [_MAX_PATH + 1];

	    _splitpath (rom_filename, drive, dir, name, ext);
	    _makepath (fname, drive, dir, name, ext);

	    strcpy (fname, S9xGetROMDirectory ());
	    strcat (fname, SLASH_STR);
	    strcat (fname, name);
	    if (ext [0])
	    {
		strcat (fname, ".");
		strcat (fname, ext);
	    }
	    _splitpath (fname, drive, dir, name, ext);
	    _makepath (fname, drive, dir, name, ext);
	    if (!Memory.LoadROM (fname))
	    {
		printf ("Error opening: %s\n", rom_filename);
        return 1;
	    }
	}
#ifdef SDD1_DECOMP
    if ((strncmp (Memory.ROMName, "Star Ocean", 10) == 0) || (strncmp(Memory.ROMName, "STREET FIGHTER ALPHA2", 21)==0))
        Settings.SDD1Pack=TRUE;
    else
        Settings.SDD1Pack=FALSE;
#endif
	Memory.LoadSRAM (S9xGetFilename(".srm"));
    //S9xLoadCheatFile (S9xGetFilename (".cht"));
    }
    else
    {
	S9xReset ();
	Settings.Paused |= 2;
    }

    CPU.Flags = saved_flags;

    if (0 && snapshot_filename)
    {
	int Flags = CPU.Flags & (DEBUG_MODE_FLAG | TRACE_FLAG);
	if (!S9xLoadSnapshot (snapshot_filename))
        return 1;
	CPU.Flags |= Flags;
    }
    return 0;
}

int snesRun(){
    S9xMainLoop ();
    return (0);
}



void S9xAutoSaveSRAM ()
{
    Memory.SaveSRAM (S9xGetFilename (".srm"));
}

void S9xExit ()
{
    S9xSetSoundMute (TRUE);
    S9xDeinitDisplay ();
    if(emu_emulating) Memory.SaveSRAM (S9xGetFilename (".srm"));
//    S9xSaveCheatFile (S9xGetFilename (".cht"));
    Memory.Deinit ();
    S9xDeinitAPU ();
}

void S9xSaveState(){
    switch( emu_state + 1 ){
        case 0:
            Snapshot(S9xGetFilename (".sav1"));
            break;
        case 1:
            Snapshot(S9xGetFilename (".sav2"));
            break;
        case 2:
            Snapshot(S9xGetFilename (".sav3"));
            break;
        case 3:
            Snapshot(S9xGetFilename (".sav4"));
            break;
        case 4:
            Snapshot(S9xGetFilename (".sav5"));
            break;
        case 5:
            Snapshot(S9xGetFilename (".sav6"));
            break;
        case 6:
            Snapshot(S9xGetFilename (".sav7"));
            break;
    }
}

void S9xLoadState(){
    switch( emu_state + 1 ){
        case 0:
            S9xLoadSnapshot(S9xGetFilename (".sav1"));
            break;
        case 1:
            S9xLoadSnapshot(S9xGetFilename (".sav2"));
            break;
        case 2:
            S9xLoadSnapshot(S9xGetFilename (".sav3"));
            break;
        case 3:
            S9xLoadSnapshot(S9xGetFilename (".sav4"));
            break;
        case 4:
            S9xLoadSnapshot(S9xGetFilename (".sav5"));
            break;
        case 5:
            S9xLoadSnapshot(S9xGetFilename (".sav6"));
            break;
        case 6:
            S9xLoadSnapshot(S9xGetFilename (".sav7"));
            break;
    }
}

bool8 S9xOpenSnapshotFile (const char *base, bool8 read_only, STREAM *file){
    char filename [PATH_MAX + 1];
    char drive [_MAX_DRIVE + 1];
    char dir [_MAX_DIR + 1];
    char ext [_MAX_EXT + 1];

    _splitpath (base, drive, dir, filename, ext);

    if (*drive || *dir == '/' ||
	(*dir == '.' && (*(dir + 1) == '/'
        )))
    {
	strcpy (filename, base);
	if (!*ext)
	    strcat (filename, ".s96");
    }
    else
    {
	strcpy (filename, S9xGetSnapshotDirectory ());
    strcat (filename, SLASH_STR);
	strcpy (filename, base);
	if (!*ext)
	    strcat (filename, ".s96");
    }

#ifdef ZLIB
    if (read_only)
    {
	if ((*file = OPEN_STREAM (filename, "rb")))
	    return (TRUE);
    }
    else
    {
	if ((*file = OPEN_STREAM (filename, "wb")))
	{
	    return (TRUE);
	}
    }
#else
    char command [PATH_MAX];

    if (read_only)
    {
	sprintf (command, "gzip -d <\"%s\"", filename);
	if (*file = popen (command, "r"))
	    return (TRUE);
    }
    else
    {
	sprintf (command, "gzip --best >\"%s\"", filename);
	if (*file = popen (command, "wb"))
	    return (TRUE);
    }
#endif
    return (FALSE);
}


void S9xCloseSnapshotFile (STREAM file){
#ifdef ZLIB
    CLOSE_STREAM (file);
#else
    pclose (file);
#endif
}

void S9xInitInputDevices ()
{
}

const char *GetHomeDirectory ()
{
    return (getenv ("HOME"));
}

const char *S9xGetSnapshotDirectory ()
{
    static char filename [PATH_MAX];
    const char *snapshot;

    if (!(snapshot = getenv ("SNES9X_SNAPSHOT_DIR")) &&
	!(snapshot = getenv ("SNES96_SNAPSHOT_DIR")))
    {
#ifdef WIN32
    char* dir = emuWindow->sSaveDir.toLatin1().data();
    return(dir);
#else
#ifdef DREAMCAST
	return("/ram");
#else
#ifdef __SYMBIAN32__
        char* dir = emuWindow->sSaveDir.toLatin1().data();
        return(dir);
#else
	const char *home = GetHomeDirectory ();
	strcpy (filename, home);
	strcat (filename, SLASH_STR);
	strcat (filename, ".snes96_snapshots");
	mkdir (filename, 0777);
	chown (filename, getuid (), getgid ());
#endif
#endif
#endif
    }
    else
	return (snapshot);

    return (filename);
}

const char *S9xGetFilename (const char *ex)
{
    static char filename [PATH_MAX + 1];
    char drive [_MAX_DRIVE + 1];
    char dir [_MAX_DIR + 1];
    char fname [_MAX_FNAME + 1];
    char ext [_MAX_EXT + 1];

    _splitpath (Memory.ROMFilename, drive, dir, fname, ext);
    strcpy (filename, S9xGetSnapshotDirectory ());
    strcat (filename, SLASH_STR);
    strcat (filename, fname);
    strcat (filename, ex);

    return (filename);
}

const char *S9xGetROMDirectory ()
{
    const char *roms;

    if (!(roms = getenv ("SNES9X_ROM_DIR")) &&
	!(roms = getenv ("SNES96_ROM_DIR")))
	return ("." SLASH_STR "roms");
    else
	return (roms);
}

const char *S9xBasename (const char *f)
{
    const char *p;
    if ((p = strrchr (f, '/')) != NULL || (p = strrchr (f, '\\')) != NULL)
	return (p + 1);

    return (f);
}

#ifndef _ZAURUS
const char *S9xChooseFilename (bool8 read_only)
{
    char def [PATH_MAX + 1];
    char title [PATH_MAX + 1];
    char drive [_MAX_DRIVE + 1];
    char dir [_MAX_DIR + 1];
    char ext [_MAX_EXT + 1];

    _splitpath (Memory.ROMFilename, drive, dir, def, ext);
    strcat (def, ".s96");
    sprintf (title, "%s snapshot filename",
	    read_only ? "Select load" : "Choose save");
    const char *filename;

    S9xSetSoundMute (TRUE);
    filename = S9xSelectFilename (def, S9xGetSnapshotDirectory (), "s96", title);
    S9xSetSoundMute (FALSE);
    return (filename);
}
#endif

#ifndef _ZAURUS
bool8 S9xInitUpdate ()
{
    return (TRUE);
}
#endif

float w,h;
extern QImage *snesFrameBuffer;
extern uint16 palete565[256];

bool8 S9xDeinitUpdate (int Width, int Height,bool8 sixnocount)
{
//    uint16* screen = (uint16*)snesFrameBuffer->bits();
//    if(Settings.SixteenBit == true){
//        for( int i = 0; i < Height; i++){
//            for( int j = 0; j < Width; j++)
//                screen[j + (i * Width)] = palete565[GFX.Screen[j + (Width * i)]];
//        }
//    } else{
//        for( int i = 0; i < Height; i++){
//            for( int j = 0; j < Width; j++)
//                screen[j + (i * Width)] = palete565[GFX.Screen[j + (Width * i)]];
//        }
//        //memcpy(screen, GFX.Screen, Width*Height);
//    }
    emuWindow->updateScreen(Width, Height);
    return(TRUE);
}

#ifndef WIN32
void _makepath (char *path, const char *, const char *dir,
		const char *fname, const char *ext)
{
    if (dir && *dir)
    {
	strcpy (path, dir);
	strcat (path, "/");
    }
    else
	*path = 0;
    strcat (path, fname);
    if (ext && *ext)
    {
        strcat (path, ".");
        strcat (path, ext);
    }
}

void _splitpath (const char *path, char *drive, char *dir, char *fname,
		 char *ext)
{
    *drive = 0;

    char *slash = (char*)strrchr (path, '/');
    if (!slash)
    slash = (char*)strrchr (path, '\\');

    char *dot = (char*)strrchr (path, '.');

    if (dot && slash && dot < slash)
	dot = NULL;

    if (!slash)
    {
	strcpy (dir, "");
	strcpy (fname, path);
        if (dot)
        {
	    *(fname + (dot - path)) = 0;
	    strcpy (ext, dot + 1);
        }
	else
	    strcpy (ext, "");
    }
    else
    {
	strcpy (dir, path);
	*(dir + (slash - path)) = 0;
	strcpy (fname, slash + 1);
        if (dot)
	{
	    *(fname + (dot - slash) - 1) = 0;
    	    strcpy (ext, dot + 1);
	}
	else
	    strcpy (ext, "");
    }
}
#endif


void S9xProcessEvents (bool){

}

void S9xSyncSpeed ()
{
    S9xProcessEvents (FALSE);
    static quint32 next1=0;
//    if (!Settings.TurboMode && Settings.SkipFrames == AUTO_FRAMERATE)
    if (Settings.SkipFrames == AUTO_FRAMERATE)
    {
        quint32 now = emuWindow->timeCounter->elapsed();

	if (!next1)
	    next1 = now;

	next1 += Settings.FrameTime;

	if ((now-AUTO_FRAMESKIP_DELAY)>next1)
	{
	    IPPU.SkippedFrames++;
	    if (IPPU.SkippedFrames > MAX_FRAMESKIP)
	    {
		IPPU.RenderThisFrame = TRUE;
		IPPU.SkippedFrames = 0;
		next1 = now + 2;
	    }
	    else
		IPPU.RenderThisFrame = FALSE;
	}
	else
	{
	    CHECK_SOUND ();
	    if ((now+AUTO_FRAMESKIP_DELAY)<next1)
	    {
#if !defined(DREAMCAST) || !defined(__SYMBIAN32__)
		do
		{
		    CHECK_SOUND ();
            now = emuWindow->timeCounter->elapsed();
		} while (next1 > now);
#endif
		next1=now;
	    }
	    IPPU.RenderThisFrame = TRUE;
	    IPPU.SkippedFrames = 0;
	}
    }
    else
    {
	next1 = 0;
	if (++IPPU.FrameSkip >= (Settings.TurboMode ? Settings.TurboSkipFrames
						    : Settings.SkipFrames))
	{
	    IPPU.FrameSkip = 0;
	    IPPU.SkippedFrames = 0;
	    IPPU.RenderThisFrame = TRUE;
	}
	else
	{
	    IPPU.SkippedFrames++;
	    IPPU.RenderThisFrame = FALSE;
	}
    }
}


#ifndef AUTO_EVENTS
void snesKeyprocess(int key, bool pressed)
{
    quint32 val=0,which=0;
        if(key == snesKeys[0]){
            which=0;
			val=SNES_UP_MASK;
        }else  if(key == snesKeys[1]){
            which=0;
			val=SNES_DOWN_MASK;
        }else  if(key == snesKeys[2]){
            which=0;
			val=SNES_LEFT_MASK;
        }else  if(key == snesKeys[3]){
            which=0;
			val=SNES_RIGHT_MASK;
        }else  if(key == snesKeys[4]){
            which=0;
			val=SNES_A_MASK;
        }else  if(key == snesKeys[5]){
            which=0;
			val=SNES_B_MASK;
        }else  if(key == snesKeys[6]){
            which=0;
			val=SNES_X_MASK;
        }else  if(key == snesKeys[7]){
            which=0;
			val=SNES_Y_MASK;
        }else  if(key == snesKeys[8]){
            which=0;
			val=SNES_TL_MASK;
            if(pressed){
				 SuperscopeCursor = 1;
            }else{
				 SuperscopeCursor = 0;
            }
        }else  if(key == snesKeys[9]){
            which=0;
			val=SNES_TR_MASK;
            if(pressed){
                MouseRbutton = 1;
            }else{
                MouseRbutton = 0;
            }
        }else  if(key == snesKeys[10]){
            which=0;
			val=SNES_SELECT_MASK;
            if( Settings.ControllerOption == SNES_SUPERSCOPE){
                if(SuperscopeTurbo){
                     SuperscopeTurbo = 0;
                     S9xSetInfoString ("SUPERSCOPE TURBO ON");
                    }else{
                     SuperscopeTurbo = 1;
                     S9xSetInfoString ("SUPERSCOPE TURBO OFF");
                }
            }
        }else  if(key == snesKeys[11]){
            which=0;
			val=SNES_START_MASK;
        }
	if (pressed)
		joypad_val[which]|=val;
	else
		joypad_val[which]^=val;

}
#endif

static int Rates[8] =
{
    //0, 8192, 11025, 22050, 44000, 36600, 16500, 29300
    //0, 8192, 11025, 22050, 44000, 36600, 16500, 29300
    0, 8000, 11025, 22050, 44100, 16000, 29300, 8000
};

static int BufferSizes [8] =
{
    0, 512, 1024, 2048, 4096, 4096, 2048, 4096
};

void update_audio(int len);

bool8 S9xOpenSoundDevice (int mode, bool8 stereo, int buffer_size)
{
    so.stereo = stereo;
    so.playback_rate = Rates[mode & 0x07];
    S9xSetPlaybackRate (so.playback_rate);

    if (buffer_size == 0)
	buffer_size = BufferSizes [mode & 7];
    if (buffer_size > MAX_BUFFER_SIZE / 4)
	buffer_size = MAX_BUFFER_SIZE / 4;
    so.buffer_size=buffer_size;

    QAudioFormat format;
    format.setFrequency(so.playback_rate);
    format.setChannels(so.stereo?2:1);
    format.setSampleSize(16);
    format.setCodec("audio/pcm");
    format.setByteOrder(QAudioFormat::LittleEndian);
    format.setSampleType(QAudioFormat::SignedInt);

    QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice());

    emuWindow->audioOut = new QAudioOutput(info, format, emuWindow);
    if(emuWindow->audioOut){
        emuWindow->setDevice();
        return true;
    }else{
        return false;
    }
}


static uint8 Buf[MAX_BUFFER_SIZE];

#define FIXED_POINT 0x10000
#define FIXED_POINT_SHIFT 16
#define FIXED_POINT_REMAINDER 0xffff

void S9xGenerateSound ()
{
    if (!emu_sound_enable)
	    return;
    int buffer_size = so.buffer_size;
    int bytes_so_far = (so.samples_mixed_so_far << 1);
    if (bytes_so_far >= buffer_size)
	return;


    so.err_counter += so.err_rate;
    if (so.err_counter >= FIXED_POINT)
    {
        int sample_count = so.err_counter >> FIXED_POINT_SHIFT;
	int byte_offset;

        so.err_counter &= FIXED_POINT_REMAINDER;
	if (so.stereo)
	    sample_count <<= 1;
	byte_offset = bytes_so_far + so.play_position;

	do
	{
	    int sc = sample_count;
	    int byte_count = sample_count;
	    byte_count <<= 1;

	    if ((byte_offset & SOUND_BUFFER_SIZE_MASK) + byte_count > SOUND_BUFFER_SIZE)
	    {
		sc = SOUND_BUFFER_SIZE - (byte_offset & SOUND_BUFFER_SIZE_MASK);
		byte_count = sc;
		sc >>= 1;
	    }
	    if (bytes_so_far + byte_count > buffer_size)
	    {
		byte_count = buffer_size - bytes_so_far;
		if (byte_count == 0)
		    break;
		sc = byte_count;
		sc >>= 1;
	    }
//	    S9xMixSamplesO (Buf, sc, byte_offset & SOUND_BUFFER_SIZE_MASK);
	    S9xMixSamples (Buf + (byte_offset & SOUND_BUFFER_SIZE_MASK), sc);
	    so.samples_mixed_so_far += sc;
	    sample_count -= sc;
	    bytes_so_far = (so.samples_mixed_so_far << 1);
	    byte_offset += byte_count;
	} while (sample_count > 0);
    }
}


void update_audio(int len)
{   
    if (!emu_sound_enable)
	    return;
    int sample_count = len>>1;
    if (so.samples_mixed_so_far < sample_count)
    {
	int byte_offset = so.play_position + (so.samples_mixed_so_far << 1);
	S9xMixSamplesO (Buf, sample_count - so.samples_mixed_so_far,
			    byte_offset & SOUND_BUFFER_SIZE_MASK);
	so.samples_mixed_so_far = 0;
    }
    else
	so.samples_mixed_so_far -= sample_count;
    /*char* datapointer = emuWindow->audioBuffer->data();
    char* bufpointer = (char*)Buf+so.play_position;
    for (int i = 0; i<len; i++){
        datapointer[i] = (int)((float)bufpointer[i] * (emu_volume * 0.2));
    }*/
    if( snesAudioBuffer)
        memcpy(snesAudioBuffer->data(),Buf+so.play_position,len);
    so.play_position = (so.play_position + len) & SOUND_BUFFER_SIZE_MASK;
}


quint32 S9xReadJoypad (int which1)
{
	return joypad_val[which1];
}

#ifndef _ZAURUS
static int S9xCompareSDD1IndexEntries (const void *p1, const void *p2)
{
    return (*(quint32 *) p1 - *(quint32 *) p2);
}

void S9xLoadSDD1Data ()
{
    char filename [_MAX_PATH + 1];
    char index [_MAX_PATH + 1];
    char data [_MAX_PATH + 1];
    char patch [_MAX_PATH + 1];

    Memory.FreeSDD1Data ();

    strcpy (filename, S9xGetSnapshotDirectory ());

    if (strncmp (Memory.ROMName, "Star Ocean", 10) == 0)
	strcat (filename, "/socnsdd1");
    else
	strcat (filename, "/sfa2sdd1");

    DIR *dir = opendir (filename);

    index [0] = 0;
    data [0] = 0;
    patch [0] = 0;

    if (dir)
    {
	struct dirent *d;
	while ((d = readdir (dir)))
	{
	    if (strcasecmp (d->d_name, "SDD1GFX.IDX") == 0)
	    {
		strcpy (index, filename);
		strcat (index, "/");
		strcat (index, d->d_name);
	    }
	    else
	    if (strcasecmp (d->d_name, "SDD1GFX.DAT") == 0)
	    {
		strcpy (data, filename);
		strcat (data, "/");
		strcat (data, d->d_name);
	    }
	    if (strcasecmp (d->d_name, "SDD1GFX.PAT") == 0)
	    {
		strcpy (patch, filename);
		strcat (patch, "/");
		strcat (patch, d->d_name);
	    }
	}
	closedir (dir);

	if (strlen (index) && strlen (data))
	{
	    FILE *fs = fopen (index, "rb");
	    int len = 0;

	    if (fs)
	    {
		// Index is stored as a sequence of entries, each entry being
		// 12 bytes consisting of:
		// 4 byte key: (24bit address & 0xfffff * 16) | translated block
		// 4 byte ROM offset
		// 4 byte length
		fseek (fs, 0, SEEK_END);
		len = ftell (fs);
		rewind (fs);
		Memory.SDD1Index = (uint8 *) malloc (len);
		fread (Memory.SDD1Index, 1, len, fs);
		fclose (fs);
		Memory.SDD1Entries = len / 12;

		if (!(fs = fopen (data, "rb")))
		{
		    free ((char *) Memory.SDD1Index);
		    Memory.SDD1Index = NULL;
		    Memory.SDD1Entries = 0;
		}
		else
		{
		    fseek (fs, 0, SEEK_END);
		    len = ftell (fs);
		    rewind (fs);
		    Memory.SDD1Data = (uint8 *) malloc (len);
		    fread (Memory.SDD1Data, 1, len, fs);
		    fclose (fs);

		    if (strlen (patch) > 0 &&
			(fs = fopen (patch, "rb")))
		    {
			fclose (fs);
		    }
#ifdef MSB_FIRST
		    // Swap the byte order of the 32-bit value triplets on
		    // MSBFirst machines.
		    uint8 *ptr = Memory.SDD1Index;
		    for (int i = 0; i < Memory.SDD1Entries; i++, ptr += 12)
		    {
            SWAP_DWORD ((*(quint32 *) (ptr + 0)));
            SWAP_DWORD ((*(quint32 *) (ptr + 4)));
            SWAP_DWORD ((*(quint32 *) (ptr + 8)));
		    }
#endif
		    qsort (Memory.SDD1Index, Memory.SDD1Entries, 12,
			   S9xCompareSDD1IndexEntries);
		}
	    }
	}
	else
	{
	    printf ("Decompressed data pack not found in '%s'.\n", filename);
	}
    }
}
#endif
